#ifndef __EVENT_CONTEXT_HPP__
#define __EVENT_CONTEXT_HPP__

#include "event_export.hpp"
#include "run_code_params.h"
#include "../judge_types/allJudgeTypes.h"
#include <optional>

struct CaseStatus {
    int memory;
    int time;
    string user_out;
    string error;
    // 可能由于没有stdFile导致不存在std_out
    std::optional<string> std_out;
    string example_in;
    int result;
};

template <class judge_type_t>
class run_code_context_t {
private:
    ProcessStatus latest_status;
    std::vector<std::function<void()>> clean_funs;
public:

    RuncodeParams params;
    judge_type_t judge_type;

    // result用于汇总测试结果，runtime_info统计最高的运行开销
    struct final_result {
        int testExample = 0;
        int passExample = 0;
        double score = 0;
    } result;
    ProcessStatus runtime_info;

    std::optional<string> first_error_user_out;
    std::optional<string> first_error_example_in;
    std::optional<string> first_error_std_out;
    std::vector<CaseStatus> all_case_status;

    // 因为会管理文件,禁止拷贝,移动后期再实现
    run_code_context_t(const run_code_context_t&) = delete;
    run_code_context_t(run_code_context_t&&) = delete;
    run_code_context_t& operator=(const run_code_context_t&) = delete;
    run_code_context_t& operator=(run_code_context_t&&) = delete;

    run_code_context_t() = default;

    run_code_context_t(const RuncodeParams& params);

    void clear();

    ProcessStatus init();

    const ProcessStatus& get_latest_status() const;

    void save_result();

    void save_result(const ProcessStatus& status);

    // 此处未考虑异常,后期应加上
    // laze instanstion
    template <typename = void>
    ProcessStatus execute() {
        auto execute_fun = [&] (auto&& event) {
            return event.execute(*this);
        };
        auto next_event = [&] (auto&& event) {
            return this->judge_type.next_event(event);
        };
        auto get_event_name = [] (auto&& event) {
            return get_class_name(event);
        };
        run_code_event_space::run_code_event_t event = run_code_event_space::initial_event{};

        while (!std::holds_alternative<run_code_event_space::end_event>(event)) {
            Log::instance().getOutput() << "<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>" << std::endl;
            Log::instance().getOutput() << std::visit(get_event_name, event) << std::endl;
            this->latest_status = std::visit(execute_fun, event);
            Log::instance().getOutput() << this->latest_status << std::endl;
            event = std::visit(next_event, event);
        }

        Log::instance().getOutput() << "result runtime_info:" << endl
                                    << this->runtime_info;

        auto format_optional_string = [](const std::optional<string>& s) -> string {
            return s ? "[" + s.value() + "]" : "null";
        };

        Log::instance().getOutput() << "result set:" << endl
                                    << "test_example:" << this->result.testExample << endl
                                    << "pass_example:" << this->result.passExample << endl
                                    << "score:" << this->result.score << endl
                                    << "first_error_output:" << format_optional_string(this->first_error_user_out) << endl
                                    << "first_error_input:" << format_optional_string(this->first_error_example_in) << endl
                                    << "first_error_stdout:" << format_optional_string(this->first_error_std_out) << endl;

        return this->latest_status;
    }

    ~run_code_context_t();
};


template <class judge_type_t>
run_code_context_t<judge_type_t>::run_code_context_t(const RuncodeParams& params) : params(params) {}

template <class judge_type_t>
void run_code_context_t<judge_type_t>::clear() {
    this->~run_code_context_t();
}

template <class judge_type_t>
ProcessStatus run_code_context_t<judge_type_t>::init() {
    if (!std::filesystem::exists(params.filename)) {
        return ProcessStatus{ 
                    .result = PARAMS_ERROR, 
                    .error = "not exists file:" + params.filename, 
                };
    }
    int ruler = params.ruler;
    if (ruler != ACM_CONTENT && ruler != OI_CONTENT) {
        return ProcessStatus{ 
                    .result = PARAMS_ERROR, 
                    .error = "unknown ruler params:" + std::to_string(ruler), 
                };
    }

    if (!params.spjCachePath.empty() && !existsFile(params.spjCachePath)) {
        Log::instance().getOutput() << "spjCachePath not exists, mkdir:" << params.spjCachePath << endl;
        mkMultiDir(params.spjCachePath, 0711);
    }
    // 若没有提供存放用户生成数据的路径则默认在临时目录下存放
    if (params.userCachePath.empty()) {
        auto format = std::filesystem::temp_directory_path().append("userCachePath.XXXXXX").string();
        auto tempDir = getTempDirectory(format);
        if (tempDir.empty()) {
            return ProcessStatus{ 
                        .result = SYS_ERROR, 
                        .error = strerror(errno),
                    };
        }
        params.userCachePath = tempDir;
        clean_funs.push_back(
            [dir = tempDir]() { std::filesystem::remove_all(dir);}
        );
    }
    if (!std::filesystem::exists(params.userCachePath)) {
        std::filesystem::create_directories(params.userCachePath);
    }

    // 若没有提供存放特判数据的路径则默认在临时目录下存放
    if (params.spjCachePath.empty()) {
        auto format = std::filesystem::temp_directory_path().append("spjCachePath.XXXXXX").string();
        auto tempDir = getTempDirectory(format);
        if (tempDir.empty()) {
            return ProcessStatus{ 
                        .result = SYS_ERROR, 
                        .error = strerror(errno),
                    };
        }
        params.spjCachePath = tempDir;
        clean_funs.push_back(
            [dir = tempDir]() { std::filesystem::remove_all(dir);}
        );
    }
    if (!std::filesystem::exists(params.userCachePath)) {
        std::filesystem::create_directories(params.spjCachePath);
    }

#ifdef ENABLE_DB_JUDGE
    if constexpr (contains_v<judge_type_t, DatabaseSelectJudge, DatabaseModifyJudge>) {
        if (params.initPath.empty()) {
            return ProcessStatus {  
                                    .result = PARAMS_ERROR,
                                    .error = "less initial path"
                                };
        }
    }
#endif

    // 若没有提供引用题目数据的路径则默认去initPath下寻找
    if (params.dataPath.empty()) {
        params.dataPath = params.initPath;
    }
    // 若没有提供引用特判数据的路径则默认去dataPath下寻找
    if (params.spjPath.empty()) {
        params.spjPath = params.dataPath;
    }

    if (params.dataPath.empty()) {
        return ProcessStatus {  
                                .result = PARAMS_ERROR,
                                .error = "less data path"
                            };
    }
    if (params.spjPath.empty()) {
        return ProcessStatus {  
                                .result = PARAMS_ERROR,
                                .error = "less spj path"
                            };
    }

    Log::instance().getOutput() << "final RuncodeParams:" << endl
                                << params << endl;

    return judge_type.init(*this);
}

template <class judge_type_t>
const ProcessStatus& run_code_context_t<judge_type_t>::get_latest_status() const {
    return this->latest_status;
}

template <class judge_type_t>
void run_code_context_t<judge_type_t>::save_result() {
    this->save_result(this->latest_status);
}

template <class judge_type_t>
void run_code_context_t<judge_type_t>::save_result(const ProcessStatus& status) {
    using std::max;
    runtime_info.time = max(runtime_info.time, status.time);
    runtime_info.memory = max(runtime_info.memory, status.memory);
    if (status.result != RUN_OK && !first_error_user_out) {
        first_error_user_out = status.output;
    }
    if (status.result != RUN_OK && !first_error_example_in) {
        first_error_example_in = fileToStr(judge_type.inFiles[judge_type.example_index]);
    }
    if (status.result != RUN_OK && !first_error_std_out) {
        auto compare_file = judge_type.get_compare_file();
        if (compare_file != "") {
            first_error_std_out = fileToStr(compare_file);
        }
    }
    if (all_case_status.size() == judge_type.example_index) {
        auto compare_file = judge_type.get_compare_file();
        all_case_status.push_back(CaseStatus{
            .memory = status.memory,
            .time = status.time,
            .user_out = status.output,
            .error = status.error,
            .std_out = compare_file.empty() ? std::nullopt : std::optional<std::string>(fileToStr(compare_file)),
            .example_in = fileToStr(this->judge_type.inFiles[this->judge_type.example_index]),
            .result = status.result,
        });
    }
}


template <class judge_type_t>
run_code_context_t<judge_type_t>::~run_code_context_t() {
    for (auto& fun : clean_funs) {
        fun();
    }
    clean_funs.clear();
}

#endif /* __EVENT_CONTEXT_HPP__ */
